/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.trailplanner;

import java.util.ArrayList;
import java.util.List;

import android.graphics.Color;
import android.util.Log;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

/**
 * Auxiliary class to define and edit Polylines.
 * 
 * Example of usage:
 * 
 * EditablePolyline ep = new EditablePolyline( map );
 * ep.addLatLong( latLng0 );
 * ep.addLatLong( latLng1 );
 * ep.addLatLong( latLng2 );
 * ep.startMovingMarker( 1, latLngN );
 * ep.continueMovingMarker( latLngO );
 * ep.continueMovingMarker( latLngP );
 * ep.endMovingMarker();
 * 
 */
public class EditablePolyline {

    private static final int LINE_WIDTH = 20;
    private static final int HUE_MAX = 360;
    private static final int ALPHA_MAX = 255;
    private static final String TAG = "EditablePolyline";
    private static final int UNDEFINDED = -1;

    // - Used during drag&drop
    // Polylines
    private Polyline mStartingPolyline;
    private Polyline mEndingPolyline;
    // LatLngs
    private List<LatLng> mStartingPoints;
    private List<LatLng> mEndingPoints;

    // Segments that connect moved marker with starting and ending polylines
    private Polyline mStartingSegment;
    private Polyline mEndingSegment;

    // Index of the moved point or marker. 
    private int mIndex;

    // - Used while adding new points
    private List<Marker> mMarkers = new ArrayList<Marker>();
    private List<LatLng> mPoints = new ArrayList<LatLng>();
    private Polyline mPolyline;
    
    private GoogleMap mMap = null;
    
    EditablePolyline( GoogleMap map ){
        mMap = map;
        mPoints.clear();
        mMarkers.clear();
        mStartingPoints = null;
        mEndingPoints = null;
        resetMap();
    }
    
    private void resetMap(){
        if( mMap != null ){
            mMap.clear();
        }
    }
    
    public static int getIndexForChar( String letter ){
        return letter.charAt(0) - 65;
    }
    
    public static String getLetterForIndex( int idx, final String caller ){
        String letter = String.valueOf( (char)(65 + idx) ); 
        // Log.d(TAG,"getLetterForIndex idx=" + idx + " letter=" + letter + " (" + caller + ")" );
        return letter;
    }
    
    public static String getMarkerTitle( int idx ){
        return getLetterForIndex(idx, "getMarkerTitle"); 
    }
    
    /**
     * Add a new point to the EditablePolyline.
     * 
     * @param latLng New point that will be added.
     */
    public void addLatLong( LatLng latLng ){
        int idx = mPoints.size();
        Log.i(TAG,"Adding new point " + idx );
        String markerName = getLetterForIndex( idx, "addLatLong" );
        Marker marker = mMap.addMarker( new MarkerOptions().position(latLng).title(markerName).draggable(true) );
        mPoints.add(latLng);
        mMarkers.add(marker);
        drawPolyline();
    }
    
    /**
     * Draw polyline.
     */
    public void drawPolyline(){
        if( mPoints.size() > 0 ){
            if( mPolyline == null ){
                mPolyline = mMap.addPolyline((new PolylineOptions()).addAll(mPoints));
            }
            else{
                mPolyline.setPoints(mPoints);
            }
        }
    }
    
    /**
     * Given all the currently available points, split the set into starting points (all points before index) 
     * and ending points (all points after index).
     */
    private void splitPoints(){
        int lastIndex = mPoints.size()-1;
        if( mIndex >= 0 && mIndex <= lastIndex ){
            if( mIndex > 0 ){
                mStartingPoints = mPoints.subList(0,mIndex);
                Log.d(TAG, "splitPoints index=" + mIndex + " #points=" + mPoints.size() + " #start=" + mStartingPoints.size() );
            }
            if( mIndex < lastIndex ){
                mEndingPoints = mPoints.subList(mIndex+1, lastIndex+1 );
                Log.d(TAG, "splitPoints index=" + mIndex + " #points=" + mPoints.size() + " #end=" + mEndingPoints.size() );
            }
        }
    }
    
    
    /**
     * Method that has to be called when a marker drag&drop starts.
     * Note that map can not be cleared while dragging a marker. This will stop drag&drop immediately.
     * @param idx
     * @param latLng
     */
    public void startMovingMarker( int idx, LatLng latLng ){
        Log.d(TAG,"startMovingMarker index=" + idx);
        mIndex = idx;
        splitPoints();
        removePolyline();
        drawStartingPolyline();
        drawEndingPolyline();
        moveDraggedMarker( latLng );
        drawSegments();
    }

    public void continueMovingMarker( LatLng latLng ){
        removeSegments();
        moveDraggedMarker( latLng );
        drawSegments();
    }

    public void endMovingMarker(){
        removeSegments();
        removeEndingPolyline();
        removeStartingPolyline();
        drawPolyline();
        mEndingPoints = null;
        mStartingPoints = null;
        mIndex = UNDEFINDED;
    }
    
    
    private void moveDraggedMarker( LatLng latLng ){
        mPoints.set( mIndex, latLng );
        mMarkers.get(mIndex).setPosition(latLng);
    }

    /**
     * Removes starting and ending segments.
     */
    private void removeSegments(){
        removeStartingSegment();
        removeEndingSegment();
    }

    /**
     * Remove starting segment.
     */
    private void removeStartingSegment(){
        if (mStartingSegment != null){
            Log.d(TAG,"removeStartingSegment mStartingSegment=" + mStartingSegment);
            mStartingSegment.remove();
            mStartingSegment = null;
        }
    }

    /**
     * Remove ending segment.
     */
    private void removeEndingSegment(){
        if (mEndingSegment != null){
            Log.d(TAG,"removeEndingSegment mEndingSegment=" + mEndingSegment);
            mEndingSegment.remove();
            mEndingSegment = null;
        }
    }

    /**
     * Removes polyline.
     */
    private void removePolyline(){
        if( mPolyline != null ){
            Log.d(TAG,"removePolyline mPolyline=" + mPolyline);
            mPolyline.remove();
            mPolyline = null;
        }
    }

    
    /**
     * Removes starting and ending polylines.
     */
    private void removePolylines(){
        removeStartingPolyline();
        removeEndingPolyline();
    }

    /**
     * Remove starting polyline.
     */
    private void removeStartingPolyline(){
        if (mStartingPolyline != null){
            Log.d(TAG,"removeStartingPolyline mStartingPolyline=" + mStartingPolyline);
            mStartingPolyline.remove();
            mStartingPolyline = null;
        }
    }

    /**
     * Remove ending polyline.
     */
    private void removeEndingPolyline(){
        if (mEndingPolyline != null){
            Log.d(TAG,"removeEndingPolyline mEndingPolyline=" + mEndingPolyline);
            mEndingPolyline.remove();
            mEndingPolyline = null;
        }
    }


    /**
     * Draw starting polyline.
     */
    public void drawStartingPolyline(){
        if( mStartingPoints != null && mStartingPoints.size() > 1 ){
            if( mStartingPolyline == null ){
                mStartingPolyline = mMap.addPolyline((new PolylineOptions()).addAll(mStartingPoints));
                Log.d(TAG,"drawStartingPolyline CREATE mStartingPolyline=" + mStartingPolyline);
            }
            else{
                mStartingPolyline.setPoints(mStartingPoints);
                Log.d(TAG,"drawStartingPolyline UPDATE mStartingPolyline=" + mStartingPolyline);
            }
        }
    }

    /**
     * Draw ending polyline.
     */
    public void drawEndingPolyline(){
        if( mEndingPoints != null && mEndingPoints.size() > 1 ){
            if( mEndingPolyline == null ){
                mEndingPolyline = mMap.addPolyline((new PolylineOptions()).addAll(mEndingPoints));
                Log.d(TAG,"drawEndingPolyline CREATE mEndingPolyline=" + mEndingPolyline);
            }
            else{
                mEndingPolyline.setPoints(mEndingPoints);
                Log.d(TAG,"drawEndingPolyline UPDATE mEndingPolyline=" + mEndingPolyline);
            }
        }
    }

    
    /**
     * Draw starting segment. (The line that joins moved marker with starting polyline)
     */
    public void drawStartingSegment(){
        if( mStartingPoints != null && mStartingPoints.size() > 0 ){
            if( mStartingSegment != null )
                removeStartingSegment();
            mStartingSegment = mMap.addPolyline((new PolylineOptions()).add(mStartingPoints.get(mStartingPoints.size()-1), mPoints.get(mIndex)));
            Log.d(TAG,"drawStartingSegment mStartingSegment=" + mStartingSegment);
        }
    }

    /**
     * Draw ending segment. (The line that joins moved marker with endting polyline)
     */
    public void drawEndingSegment(){
        if( mEndingPoints != null && mEndingPoints.size() > 0 ){
            if( mEndingSegment != null ) 
                removeEndingSegment();
            mEndingSegment = mMap.addPolyline((new PolylineOptions()).add(mPoints.get(mIndex), mEndingPoints.get(0)));
            Log.d(TAG,"drawEndingSegment mEndingSegment=" + mEndingSegment);
        }
    }
    
    /**
     * Reconnect moved marker with starting and ending polylines.
     */
    public void drawSegments(){
        drawStartingSegment();
        drawEndingSegment();
    }

//    private void setUpPolylineOnMap( GoogleMap map ) {
//
//        // A simple polyline with the default options from Melbourne-Adelaide-Perth.
//        map.addPolyline((new PolylineOptions())
//                .add(MELBOURNE, ADELAIDE, PERTH));
//
//        // A geodesic polyline that goes around the world.
//        map.addPolyline((new PolylineOptions())
//                .add(LHR, AKL, LAX, JFK, LHR)
//                .width(5)
//                .color(Color.BLUE)
//                .geodesic(true));
//
//        // Circle centered at Sydney.  This polyline will be mutable.
//        PolylineOptions options = new PolylineOptions();
//        int radius = 5;
//        int numPoints = 100;
//        double phase = 2 * Math.PI / numPoints;
//        for (int i = 0; i <= numPoints; i++) {
//            options.add(new LatLng(SYDNEY.latitude + radius * Math.sin(i * phase),
//                    SYDNEY.longitude + radius * Math.cos(i * phase)));
//        }
//        int color = Color.RED;
//        mMutablePolyline = map.addPolyline(options
//                .color(color)
//                .width(LINE_WIDTH));
//
//        // Move the map so that it is centered on the mutable polyline.
//        map.moveCamera(CameraUpdateFactory.newLatLng(SYDNEY));
//    }


//    @Override
//    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//        if (mMutablePolyline == null) {
//            return;
//        }
//
//        if (seekBar == mColorBar) {
//            mMutablePolyline.setColor(Color.HSVToColor(
//                    Color.alpha(mMutablePolyline.getColor()), new float[] {progress, 1, 1}));
//        } else if (seekBar == mAlphaBar) {
//            float[] prevHSV = new float[3];
//            Color.colorToHSV(mMutablePolyline.getColor(), prevHSV);
//            mMutablePolyline.setColor(Color.HSVToColor(progress, prevHSV));
//        } else if (seekBar == mWidthBar) {
//            mMutablePolyline.setWidth(progress);
//        }
//    }
}
